<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="utf-8">
	<meta http-equiv="content-type" content="text/html; charset=utf-8">
	<title>电子价签蓝牙控制器</title>
	<style>
		#tool-box>* {
			margin-right: 5px;
		}
		* {
			padding: 0;
			margin: 0;
		}
		button,a {
			padding: 2px;
			margin: 2px;
			display: inline-block;
		}
		body {
			padding: 20px;
			overflow-y: hidden;
		}
	</style>
	<script type="application/javascript" src="js/dithering.js"></script>
	<script type="application/javascript" src="js/utils.js"></script>
</head>

<body>
	<script>
		let bleDevice;
		let gattServer;
		let epdService;
		let rxtxService;
		let epdCharacteristic;
		let rxtxCharacteristic;
		let reconnectTrys = 0;
		let isSend=0;
		function delay(delayInms) {
		  return new Promise(resolve => {
			setTimeout(() => {
			  resolve(2);
			}, delayInms);
		  });
		}

		function resetVariables() {
			gattServer = null;
			epdService = null;
			epdCharacteristic = null;
			rxtxCharacteristic = null;
			rxtxService = null;
			document.getElementById("log").value = '';
		}

		async function handleError(error) {
			console.log(error);
			resetVariables();
			if (bleDevice == null)
				return;
			if (reconnectTrys <= 5) {
				reconnectTrys++;
				await connect();
			}
			else {
				addLog("Was not able to connect, aborting");
				reconnectTrys = 0;
			}
		}

		async function sendCommand(cmd) {
			if (epdCharacteristic) {
				await epdCharacteristic.writeValueWithResponse(cmd)
			} else {
				addLog('服务不可用。蓝牙链接上了吗？')
			}
		}

		async function clearScreen(cmd) {
			addLog('刷屏')
			await triggerEpdCmd(`00${cmd}`);
			await triggerEpdCmd('01')
		}

		async function rxTxSendCommand(cmd) {
		  if (rxtxCharacteristic) {
			await rxtxCharacteristic.writeValueWithResponse(cmd);
		  } else {
			  addLog('服务不可用。蓝牙链接上了吗？')
		  }
		}

		async function upload_tiff_image() {
			const startTime = new Date().getTime();
			const buffer = await document.getElementById('tiff_file').files[0].arrayBuffer();
			const arr = bytesToHex(buffer);

			addLog(`开始上传tiff, 大小 ${arr.length/1024}KB`);

			await sendCommand(hexToBytes("0000"));

			await sendCommand(hexToBytes("020000"));

			const step = 480;
			let partIndex = 0;
			for (let i = 0; i < arr.length; i += step) {
				addLog(`正在上传第${partIndex+1}块. 块大小: ${step + 1}byte`);
				await sendCommand(hexToBytes("03" + arr.slice(i, i + step)));
				partIndex += 1;
			}

			await sendCommand(hexToBytes("04"));
			addLog(`上传tiff完成，耗时${(new Date().getTime() - startTime)/1000}s`);
		}

		async function sendBufferData(value, type) {
			addLog(`开始发送图片模式:${type}, 大小 ${value.length/2/1024}KB`);
			let code = 'ff';
			if (type === 'bwr') {
				code = '00';
			}
			const step = 480;
			let partIndex = 0;
			for (let i = 0; i < value.length; i += step) {
				addLog(`正在发送第${partIndex+1}块. 块大小: ${step/2 + 4}byte. 起始位置: ${i/2}`);
				await sendCommand(hexToBytes("03" + code + intToHex(i / 2, 2) + value.substring(i, i + step)));
				partIndex += 1;
			}
		}

		async function upload_image() {
			
			if(isSend==1){
				addLog("正在上传数据中！");
				return;
			}
			isSend=1;
			addLog("开始上传数据中,请勿重复点击");
			const canvas = document.getElementById('canvas');


			const startTime = new Date().getTime();


			

			const size = document.getElementById('screensize').value;
			if("112*112"===size){
				await sendCommand(hexToBytes("0401"));
			}else if("384*184"===size){
				await sendCommand(hexToBytes("0402"));
			}else if("300*400"===size){
				await sendCommand(hexToBytes("0401"));
			}else if("480*648"==size){
				await sendCommand(hexToBytes("0402"));
			}
			await sendCommand(hexToBytes("0000"));
			await sendCommand(hexToBytes("020000"));
			await delay(100);
			await sendBufferData(bytesToHex(canvas2bytes(canvas)), 'bw')
			await sendBufferData(bytesToHex(canvas2bytes(canvas, 'bwr')), 'bwr')
			// await sendCommand(hexToBytes("0400"));
			await sendCommand(hexToBytes("0101"))
			
			isSend=0;
			addLog(`刷新完成，耗时${(new Date().getTime() - startTime)/1000}s`);
			
		}

		async function upload_epd_buffer() {
			const startTime = new Date().getTime();
			const value = document.getElementById('buffer').value.replace(/(?:\r\n|\r|\n|,|0x| )/g, '');

			addLog(`开始刷新屏幕内存, 大小 ${value.length/1024}KB`);

			await sendCommand(hexToBytes("0000"));

			await sendCommand(hexToBytes("020000"));

			await sendBufferData(value, 'bw');

			await delay(150);

			await sendCommand(hexToBytes("0101"))

			addLog(`刷新完成，耗时${(new Date().getTime() - startTime)/1000}s`);
		}

		async function setTime() {
			const { unixNow, localeTimeString, year, month, day, week } = getUnixTime();

		  	addLog("时间设置为: " + localeTimeString + " : dd" + intToHex(unixNow, 4));
		  	await rxTxSendCommand(hexToBytes('dd' +
					[intToHex(unixNow, 4), intToHex(year, 2), intToHex(month, 1), intToHex(day, 1), intToHex(week, 1)].join('')));

			await rxTxSendCommand(hexToBytes('e2'))
		}

		async function triggerRxTxCmd(cmd) {
			addLog(`发送指令: ${cmd}`)
			await rxTxSendCommand(hexToBytes(cmd));
		}

		async function triggerEpdCmd(cmd) {
			addLog(`发送指令: ${cmd}`)
			await sendCommand(hexToBytes(cmd));
		}

		function disconnect() {
			resetVariables();
			isSend=0;
			addLog('链接已断开.');
			document.getElementById("connectbutton").innerHTML = '链接';
		}

		async function preConnect() {
			if (gattServer != null && gattServer.connected) {
				if (bleDevice != null && bleDevice.gatt.connected)
					bleDevice.gatt.disconnect();
			}
			else {
				reconnectTrys = 0;
				bleDevice = await navigator.bluetooth.requestDevice({ optionalServices: ['0000221f-0000-1000-8000-00805f9b34fb', '00001f10-0000-1000-8000-00805f9b34fb', '13187b10-eba9-a3ba-044e-83d3217d9a38'], acceptAllDevices: true });
				await bleDevice.addEventListener('gattserverdisconnected', disconnect);
				try {
					await connect();
				} catch (e) {
					await handleError(e);
				}
			}
		}

		async function connectRXTX() {
			rxtxService = await gattServer.getPrimaryService('00001f10-0000-1000-8000-00805f9b34fb');
			addLog('> 找到串口服务');

			rxtxCharacteristic = await rxtxService.getCharacteristic('00001f1f-0000-1000-8000-00805f9b34fb');
			addLog('> 串口服务已链接');
			
			await rxtxCharacteristic.startNotifications();
			rxtxCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
					console.log('epd ret', bytesToHex(event.target.value.buffer))
					hex = bytesToHex(event.target.value.buffer)
					addLog(`> [来自串口]: 收到${hex}`);
				});
			
		}

		async function reConnect() {
			connectTrys = 0;
			isSend=0;
			if (bleDevice != null && bleDevice.gatt.connected)
				bleDevice.gatt.disconnect();
			resetVariables();
			addLog("重新链接");
			setTimeout(async function () { await connect(); }, 300);
		}

		async function connect() {
			if (epdCharacteristic == null) {
				addLog("正在链接: " + bleDevice.name);

				gattServer = await bleDevice.gatt.connect();
				addLog('> 找到GATT服务器');

				epdService = await gattServer.getPrimaryService('13187b10-eba9-a3ba-044e-83d3217d9a38');
				addLog('> 找到可用服务');

				epdCharacteristic = await epdService.getCharacteristic('4b646063-6264-f3a7-8941-e65356ea82fe');
				addLog('> 服务已连接');

				await epdCharacteristic.startNotifications();

				epdCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
					console.log('epd ret', bytesToHex(event.target.value.buffer))
					const count = parseInt('0x'+ bytesToHex(event.target.value.buffer));
					addLog(`> [来自屏幕]: 收到${count} byte数据`);
				});

				document.getElementById("connectbutton").innerHTML = '断开';
				await connectRXTX();
				addLog('> 连接完成');
			}
		}

		function setStatus(statusText) {
			document.getElementById("status").innerHTML = statusText;
		}

		function addLog(logTXT) {
			const today = new Date();
			const time = ("0" + today.getHours()).slice(-2) + ":" + ("0" + today.getMinutes()).slice(-2) + ":" + ("0" + today.getSeconds()).slice(-2) + " : ";

			const dom = document.getElementById("log");

			dom.innerHTML += time + logTXT + '<br>';
			dom.scrollTop = dom.scrollHeight;
		}

		function getUnixTime() {
			const hourOffset = document.getElementById('hour-offset').value;
		  	const unixNow = Math.round(Date.now() / 1000)+(60*60*hourOffset)  - new Date().getTimezoneOffset() * 60;

			const date = new Date((unixNow + new Date().getTimezoneOffset() * 60)*1000);
		  	const localeTimeString = date.toLocaleTimeString();

			return {unixNow, localeTimeString, year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate(), week: date.getDay() || 7}
		}

		async function update_image () {
			const image_file = document.getElementById('image_file');
			if (image_file.files.length > 0) {
				const file = image_file.files[0];

				const canvas = document.getElementById("canvas");
				const ctx = canvas.getContext("2d");

				const image = new Image();
				image.src = URL.createObjectURL(file);
				image.onload = function(event) {
					URL.revokeObjectURL(this.src);
					ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
					convert_dithering()
				}
			}
		}

		function get_position(canvas, x, y){
			let rect = canvas.getBoundingClientRect()
			return {
				x: x - rect.left * (canvas.width/rect.width),
				y: y - rect.top * (canvas.height/rect.height)
			}
		}

		function clear_canvas() {
			if(confirm('确认清除屏幕?')) {
				const canvas = document.getElementById('canvas');
				const ctx = canvas.getContext("2d");
				ctx.fillStyle = 'white';
				ctx.fillRect(0, 0, canvas.width, canvas.height);
			}
		}

		function convert_dithering() {
			const canvas = document.getElementById('canvas');
			const ctx = canvas.getContext("2d");
			const mode = document.getElementById('dithering').value;
			if (mode.startsWith('bwr')) {
				ditheringCanvasByPalette(canvas, bwrPalette, mode);
			} else {
				dithering(ctx, canvas.width, canvas.height, parseInt(document.getElementById('threshold').value), mode);
			}
		}

		document.body.onload = () => {
			setInterval(() => {
				const { localeTimeString, year, month, day, week } = getUnixTime();
				document.getElementById('time-setter').innerText = `设置时间为：${year}-${month}-${day} ${localeTimeString} 星期${week}`;
			}, 1000);

			const canvas = document.getElementById('canvas');
			const ctx = canvas.getContext("2d");
			ctx.fillStyle = 'white';
			ctx.fillRect(0, 0, canvas.width, canvas.height);

			let is_allow_drawing = false;
			let is_allow_move_editor = false;
			const image_mode = document.getElementById('canvas-mode');
			const paint_size = document.getElementById('paint-size');
			const paint_color = document.getElementById('paint-color');
			const editor = document.getElementById('edit-font');
			const font = document.getElementById('font');
			document.getElementById('dithering').value = 'bwr_Atkinson';
			document.getElementById('screensize').value = '300*400';
			const size = document.getElementById('screensize').value;
			console.log(size)
			width = parseInt(size.split('*')[0])
			height = parseInt(size.split('*')[1])
			document.getElementById('canvas').width = width;
			document.getElementById('canvas').height = height;
			image_mode.value = 'paint';
			paint_color.value = 'black';
			font.value = '黑体';

			editor.onmousemove = function (e) {
				editor.style.fontSize = `${paint_size.value * 10}px`;
				editor.style.color = paint_color.value;
				editor.style.fontFamily = font.value;
				editor.style.fontWeight = 'bold';

				if (is_allow_move_editor) {
					const {x, y} = get_position(canvas, e.clientX, e.clientY);
					if (x < 0 || y < 0 || x > canvas.width || y > canvas.height) {
						return;
					}

					editor.style.left = `${e.clientX-20}px`;
					editor.style.top = `${e.clientY-20}px`;

				}
			}

			editor.onmousedown = function (e) {
				is_allow_move_editor = true;
			}

			editor.onmouseup = function (e) {
				is_allow_move_editor = false;
			}

			document.getElementById('update-text').onclick = function () {
				if (!editor.value.length) {
					alert('请先输入文字');
					return;
				}
				editor.style.display = 'none';
				ctx.beginPath();
				ctx.font = `bold ${paint_size.value * 10}px ${font.value}`;
				ctx.fillStyle = paint_color.value;
				const {x, y} = get_position(canvas, parseInt(editor.style.left), parseInt(editor.style.top) + paint_size.value * 10);

				ctx.fillText(editor.value, x, y);
			}
            document.getElementById('screensize').onchange = function () {
				const size = document.getElementById('screensize').value;
				console.log(size)
				width = parseInt(size.split('*')[0])
				height = parseInt(size.split('*')[1])
				document.getElementById('canvas').width = width;
				document.getElementById('canvas').height = height;
			}
			image_mode.onchange = function (e) {
				if (image_mode.value === 'font') {
					document.getElementById('update-text').style.display = 'inline-block';
					document.getElementById('font').style.display = 'inline-block';

					editor.style.display='block';
					editor.style.left = `${e.clientX}px`;
					editor.style.top = `${e.clientY}px`;
					return;
				}
				document.getElementById('update-text').style.display = 'none';
				document.getElementById('font').style.display = 'none';
				editor.style.display='none';
			}

			paint_size.onchange = function () {
				if (image_mode.value === 'font') {
					editor.style.fontSize =  `${paint_size.value * 10}px`;
				}
			}

			paint_color.onchange = function () {
				if (image_mode.value === 'font') {
					editor.style.color = paint_color.value;
				}
			}

			font.onchange = function () {
				if (image_mode.value === 'font') {
					editor.style.fontFamily = font.value;
				}
			}

			canvas.onmousedown = function(e) {
				let ele = get_position(canvas, e.clientX, e.clientY)
				let { x, y } = ele

				switch (image_mode.value) {
					case 'paint':
						is_allow_drawing = true;
						ctx.beginPath();
						ctx.moveTo(x, y);
						break;
					case 'font':
						editor.style.display='block';
						editor.style.left = `${e.clientX}px`;
						editor.style.top = `${e.clientY}px`;
						editor.style.fontSize = `${paint_size.value * 10}px`;
						editor.style.color = paint_color.value;
						editor.style.fontFamily = font.value;
						editor.style.fontWeight = 'bold';

						break
					default:
						break;
				}
			};

			canvas.onmousemove = (e) => {
				let ele = get_position(canvas, e.clientX, e.clientY)
				let { x, y } = ele;
				switch (image_mode.value) {
					case 'paint':
						if (is_allow_drawing) {
							ctx.lineWidth = paint_size.value;
							ctx.strokeStyle=paint_color.value;
							ctx.lineTo(x, y);
							ctx.stroke();
						}
						break;
					case 'font':
						break;

					default:
						break;
				}
			}

			canvas.onmouseup = function() {
				switch (image_mode.value) {
					case 'paint':
						is_allow_drawing = false;
						break;

					case 'font':
						editor.focus();
						is_allow_move_editor = false;
						break;

					default:
						break;
				}
			}

			canvas.onmouseleave = function () {
				if (image_mode.value === 'paint') {
					is_allow_drawing = false;
				}
			}
		}
	</script>
	 <h2>电子价签蓝牙控制器<a style="margin-left: 130px;" href="ATC_TLSR_Paper_UART_Flasher.html">串口烧录</a><a href="ATC_TLSR_Paper BLE control.html">蓝牙OTA升级</a>
	</h2>


	<button id="connectbutton" type="button" onclick="preConnect();">链接</button>
	<button type="button" onclick="reConnect();">重新链接</button>
	<button type="button" onclick="document.getElementById('log').innerHTML = '';">清空日志</button>
	<br><br>

	<h3>指令控制</h3>
	<input type="text" id="cmdTXT" value="0055">
	<button type="button" onclick="triggerEpdCmd(document.getElementById(&quot;cmdTXT&quot;).value);">发送屏幕指令</button>
	<br>
	<input type="text" id="cmdTXT2" value="FF">
	<button type="button" onclick="triggerRxTxCmd(document.getElementById(&quot;cmdTXT2&quot;).value);">发送CMD指令</button>
	<br>
	<button type="button" onclick="triggerRxTxCmd('e100')" title="点击该按钮，然后上传图片。图片将永久显示到屏幕上">设置为图片模式</button>
	<!-- <button type="button" onclick="triggerRxTxCmd('e101')" title="点击该按钮, 切换为时钟模式">设置为时钟模式1</button> -->
	<button type="button" onclick="triggerRxTxCmd('e101')" title="点击该按钮, 切换为时钟模式">设置为日历模式</button>
	<button type="button" onclick="triggerRxTxCmd('e102')" title="点击该按钮, 切换为时钟模式">设置为日历极简模式</button>
	<button type="button" onclick="triggerRxTxCmd('e2')" title="刷新屏幕">刷新屏幕</button>
	<button type="button" onclick="triggerRxTxCmd('DF')" title="保存配置">保存配置</button>
	<button type="button" onclick="triggerRxTxCmd('03')" title="启用自定义LOGO">启用自定义LOGO</button>
	<button type="button" onclick="triggerRxTxCmd('04')" title="使用默认LOGO">使用默认LOGO</button>
	<button type="button" onclick="triggerRxTxCmd('00')" title="重启价签">重启价签</button>
	<!-- <button type="button" onclick="clearScreen('00')">清屏全黑</button>
	<button type="button" onclick="clearScreen('ff')">清屏全白</button>
	<button type="button" onclick="triggerEpdCmd('0101')">刷新屏幕(图片模式全屏更新)</button>
	<button type="button" onclick="triggerEpdCmd('0100')">刷新屏幕(图片模式局部更新)</button> -->
	<br><br>

	<h3>上传图片到屏幕</h3>
	<input type="file" id="image_file" onchange="update_image()" accept=".png,.jpg,.bmp,.webp,.jpeg">
	抖动算法：
	<select id="dithering" title="抖动算法">
		<optgroup label="黑白">
			<option value="none">二值化</option>
			<option value="bayer">bayer</option>
			<option value="floydsteinberg">floydsteinberg</option>
			<option value="Atkinson">Atkinson</option>
		</optgroup>
		<optgroup label="黑白红多色">
			<option value="bwr_floydsteinberg">黑白红floydsteinberg</option>
			<option value="bwr_Atkinson">黑白红Atkinson</option>
		</optgroup>
	</select>
	</select>
	屏幕尺寸：
	<select id="screensize" title="模式">
		<option value="384*184">3.5(384*184)</option>
		<option value="112*112">logo(112*112)</option>
		<option value="300*400">4.2(300*400)</option>
		<option value="480*648">5.83(480*648)</option>
	</select>
	阈值:
	<input type="number" max="255" min="0" value="125" id="threshold">
	<button onclick="update_image()">重新加载</button>
	<br>

	<div id="canvas-box">
		<div id="tool-box">
			模式：
			<select id="canvas-mode">
				<option value="paint">画笔</option>
				<option value="font" title="输入完成后 点击：保存文本框">输入文字</option>
			</select>
			画笔/文字大小：
			<input type="number" max="13" min="1" step="1" value="3" id="paint-size">
			画笔颜色：
			<select id="paint-color">
				<option value="red">红色</option>
				<option value="white">白色</option>
				<option value="black">黑色</option>
			</select>

			<select id="font" title="字体" style="display: none;">
				<option value="微软雅黑">微软雅黑</option>
				<option value="黑体">黑体</option>
				<option value="仿宋">仿宋</option>
				<option value="宋体">宋体</option>
				<option value="楷体_GB2312">楷体_GB2312</option>
				<option value="华文行楷">华文行楷</option>
			</select>
			<button id="update-text" style="display: none">保存文本框</button>
			<button onclick="clear_canvas()">清屏</button>
		</div>
		<input id="edit-font" style="max-width: 384px; position: absolute; border: black solid 1px;background-color: rgba(0,0,0,0);display: none;overflow: auto" />
		<canvas id="canvas" width="384" height="184" style="border: black solid 1px;"></canvas>
		<br>
		<button onclick="upload_image()" style="font-weight: bold">立即上传到屏幕</button>
		<br>
	</div>

	<br>
	<h3>设置时间</h3>
	<button id="time-setter" onclick="setTime()">设置时间为: </button>	<br>
	偏移+<input type="number" max="24" min="0" id="hour-offset" value="0" style="width: 40px;">小时

	<!-- <div id="tab_image">
		<br>
		<h3>屏幕控制</h3>
		<input type="file">
		<button type="button" onclick="upload_epd_buffer();">上传图片到显示器内存</button>
		<br>
		<textarea id="buffer" rows="10" cols="90">00240219fffed5094098000000000000828ab4897f6b6aa129420000000a824008290042f7db6a8884a89248000554141244aa567e255624281440012490a10000d2400c790aa2a901020010000082822a091112554a954250480880000454500006490e4110097908802402a950a90a55724250041552a821008014020055400000205510420480010800a0000154105aa97008a111211088000aa000002982285444201404080200000550201240482a80a88042a000080800aa0002a814220400020128020a202005520080044108002000400228008020220a40000014420000000400805501001aa9040400010900000000000002441164ab4000012800000000000002900026aaa5100012014248008000040015203b5556422048000400020400104040034d52291909000000004000204000955ca9a954aaa0000200000000000000225655440220000000000000000000040bfad2bad0a4088080200000000200103edd1ad50500001012020000110001411bf569b892810000000002044000000e7f6e98ee0810248005e000010008050097dbaebba08000024a9500000000202f5d7555de55020008154000104493c2801fddb56a30882240ba9400012101e1af776b6db9670000084a50000044ebe923ddf6dad48a000023da9501001cc7cc477badb774a0088901baaa00159d61cc54ed76ddca1020000bd94900505effdd41bedb6b6142000096b6a40a866ec7ec40f5adbda00001222ad555c12915eff84ffed6d6c2504000153aaa6094abefffc35b7b6d820204047aca9448c241ffbad0fdadb748900010ed5552203b55ffffd476d6ddd2802041fb5555948ca8fbff63bf6b6b13120202beaaaa4011056ffe773dbdbd6620000af6aaaaac016177ff9fdad6d78d691003ddaaa921c2a9d7ff5f7f5b7a0cf80116f755555498970bfe5feaeda90ada400b5fd5544299214fff9dfb56dcb5fd042d556aa24d4c0493efffb6fb760ad5001adfaa5512149a0afffffdadac55fb10ab7df552c95a2153fffeef56f6116c0035f6954952c449157ffffdfbba54d5023ebffaaa804a595afdfff756d6a97a207eaed5545688068f3fff7eef7b1554043f5b564a2126a9487bfdff5ad4292b407def5eaa144905051dbfedef7b74e8503f3556ab49242820f6fffeb5aa75a6207dfeaaa4248a228a2bfff5bb7c54aa813eab555550a1085177fffed684b3a4003dbd755584cd350b9bfff5bda5eea5243fa79aaa20aa8a246dffbeeb572595003f596abd24528293abfdeb49459749403eed2a8b09a5490577dff92c26dca4121faaaab7418a28a5af7fe5438bab52801d55655d10645152daf79558a2ed95280faab52b624610ab6b5caaad53aa950207d56aad08024244b5555552a5d51488277ad52702100811a54aaaad728ac64003ed6a92584025996ab55552fb25699103ffaa570029054db4aaaabff912954409f5d55100000496bf956adffa0aa52823fab54e220424aaddf5b5bff8868aa000fb5ab300408277621495fff8d2555240edd54a0002009ab9d6d7dff902455400756ab512082455fe1253bff80550a1042b56ca002016965fa29bbff90624a810555b55000088126ffcebdfed6480a481eca42511000a4ab7fc17fffba6013205ba2fda001201115fd74bbfbae00808a5ee9052000104601efda1bee7b9105a92bb27bd209422495beb749bddefcf294beec0da020a08853ffdda7f377bff1614aba9ba000a2025beab6d08eddefd9282f4d2ab0004a2457bf6f55fbf77b7d914af2974002008a1bd5dac35ebdeffee12fbaa6b090242356ff774eebd7bdeed282ee16a00040413bb7bbebbefeffbf082b5ab552110129affded4debb7d6fc5e00a054a4080422fb6fb79ebddd7ff7ba42ab2ea02210054ffefad7d777fbbfdd1aa8b55404408a6b7bef5afddedefffe65546ab04520354beffabf5777f7f6f50007db5122a40a137fb7aafddd5fbff2a685b5700061046eddf53faf7ffaffd19507daa11280522177d55575d5bff6dc86d2edd0480880455f746ddf7fed17beb543ab6020a28db81ff8deebdab7fb7e4aa1f6b2848400777bd4777d77effd769b41add000405143bf785bafbedffa9ecd51f761090a0f62fdedf6f547deef5f6544baf0040112ebf7db5daf0177bbf77550ed540021937dffeeeefa01d6fbfbf549fa724044c7b5dd77b7a802da997dbaa8eab00840b7ffffbadaf001bf665eb8a875540104bf5ff6d77f5017d7ff07dca15ea040121db6dfbdd5e006fb7d695c58f6a400081fbffdeb7ea0857df626ce27d0a092425fffff5ed5c00eea78bff75faf5000191ffdb7b5bf6017f7d5fe1aff3ba4002043effd77d5e10bdaabdae3fc36e1048a2b7fffdd7ea0037ab6f55bf07fb4009b16feed6babe006f7fd895fd415d01020419fffbefd701e5ffaeabf637f64051495dfbfcb57a059a6adf5779515a00580273bfb73faf8067bdbf93e059eb044ca86fffffc57ac00fcaffe5d4a1b4007e8aeff6fdf9dfe01afff7e048215a103fdcf5dfefb6b5521fffbef99289aa013ff8ffffffff4bfdf6aafbfe8810d5025dfafffdbb6af4b77fffeffaa54aba131fe67fbfffffbe9ddbffff2ec000c9082ffdfdf7dfdaebe2faad1df33c40502207fdfffefb7f7ebd177e4afdd710de0297feffffeff5abefe9d57afe77486b119fffffffffdefebafff8ff771db4fd0a7fffffffbef7abefaa7eaf1b96d22aa51ffffffffffafebdffaff3edf7287f153fffffffffaf57ef6fdbfdced1d66aa55ffffffffbfbfabbfad7fafeb3b835555fffffffff7d5feeaff7fdef067236955fffffffffefeabbff77feeff2fa5b555ffffffff7fd7feedbdfdd7e35f835aabffffffffeefaabffef7fef75f843a555ffffffffffdffea0117fef37e762faaafffffffffb6aab154a7fe7f5aa875555dffffff77ffff0aaa95bd76edde1daaa7fffffdfffd50a91253f56fd56a3652afffffffffffd4a4c94958f5e8d01baaaffffffbdffa8a552aaaabebc4002d5555dffffddefe5292a494a95d5ea81f95577dfffbdffd4a4a5520f7bfe600355555dfffffdff8a95512952dfff62d1eaaaa5ffefbdfe29552ffffda3754d4175555a7ff7dbf8a5207ffffe12de46a9aa92a5aeffdff55409ffffff41219b50f4aaaabff7bbe492d7fffffe545ccd69a555555bfbdbcaaa4fffdfff23220ea9faaaaaaafbdba4953ffdfeefac954b50a555556dfbdb15497feffffffea92951faaaab557dda52557f73fbfffaa55250a54a5556fdc14aa5fdc7effff4548924f4aaaaab7d14a92bffaffffbf54a6a915aaaaab5be829557ef2fbfefb5291244e555555af055524efebfffbff4a54928b529556b4488954fef5ffffffa955290ea820aad1455493f8f1dfdfff9524c54b454a2aaaa892abf5fafeffff24aa2a4d12aa954a264a4be5fafffff79281542694494925f15557cbedffffdfca6e490742a4a453faa4afabfffbfeffc29120558915124bfa528fd7fffff7ffe8a4aa8660a4a9571a955fcbffefdfffe208904b9a0005261a492eefdfbffffff8a0a286492a9253b9549fe3fefffffdfc0b292b55400129f5255ff7ffff7fefdf208086aaa8a54449555ff3f3ffff7fff882408552b522b24893fffa7fdfdffffe20905495555a8aaa4bdffd3f7fffffbfda0025552244552553fdfd7dfffff7ffed00aaa9ff2aa4a9577ffaffff7fdfffb6a241457f5252952bfffa7fffff7feedb00281fff2c94a493fedd7ffdfdff7fed08a6e8bf41412aabf67cf7f7ffffffea205927fe9458924bfd7e5f7ff7ffffdd02254efe5520a927d8febffffffffdeb08953fff4a90955375ff7ffffff777d6404abffe25514917f4ffffffdfdfffdb025482fb5248a4abf2f7fdddffffffed102574ae95252a93f5fffffffeffff560485155448a49257f4fff7ffffff7dfb0024d52b2692494bbadfbffffbfdf7d5248aaaa951555527fd7d9fbbbff7ffad002525552a4924abfcbf5fffffdffff70104aaaa49249293fe7e5effffffff55249554aaa555554bbffebffffeffdfad800aaa00149248abfffd3ff777ff7db6824a41552a452a93f7f6bfdffffdff5a812d2a4a915ea549ffde7bffffeffe82510e49244abb522bfeff3fffffbff858042924aaa4619555dfff5f7eeeffe5452088aa9112b1a491ff9f9dfbffffca50520d124a427b924afb5fdffffffda50a08784928113faaaaef3ffffffff790a8945e0482849f49247e9dffdfbbbe5544216f5028204a5492be5ff6fdfffe9495045bc900aa2292a97f5fdffffffd4a22406efc95548849149e9f7ff7fdec212914bfb755540104425f5defdfdffaacca404bfd556b5421092f8fc77fffb9422492ded6aaad0008405dd7b7ff7df89942416ffd5555c4421207cb8ffdf7f245294bbb6eaaaa702080a7f3eff7fff9349406dffaaaab99042a017b96dfffb08a41236dad5554d6228051bfcbffdee9512805affa95575a8012046ae3ff7fd80a92275b6d5559696240912bbbfdfb6ca04083bff55556ad8c14041568d7edaa0a0806ddbd2aaaa96e815086af6d56b520824f7feea955b594e40221d555b55a84280badbb5aaaaccaa1200835b6add770b08ebfed5d56d55e700a821adad6baaf402ddef6aeaab18aad40208356bad5b5400f7fddf7daad5674148a005b5556d50235dff75d6ed2db5e80002005ef6b5000177eddf7fb5b4d6b92a90a400098880099eff77eaeaa955d40004012100232882ebffdd7fbb2db6bf49212804490f0003b6faf7dbead4d36a80484140002520157bffbef6f5756db7ea02081554ae0087adfff7bfbb59b6dd7890824001d45c0aeaeeddedd5555577af0a280926b83447dbffff7f6dabb7adfde081200dedd60fafff77d5eab55af6affe0404f75d580d76fdfdffb5aeed5bf56ff1555afdb43f6fffdf6eeab557bd5ffaafaab753502af5f777fbbb56dad7edbdbadadaa92277bafffedfed6b6d6977d6d5556b5688dfedfdeffab5ad57b6bd7b6d5aaaaa40aaf7efbddff555bad8d7adb555564513f7aafffffb6d6ed6adbb5ed6b555b2a2bd7dfef76ed5b56dd5adfb5bd52aba87efaafffffdb6ab5b2a6b2dad755aa50d5ed6ffddf75aadb6d696b6e8daad500ff76ffbffddd5756db9d557576d2aac9f7dbafffffeab56db155551695aaa442ab56dfffeebd55b6dd2540da4e9d450b7db77fff7beb5aab6a9354ae4aaab202ef6daffdffdaab5aba54a96b1575481ff5ae7f77eeead56dd54382a5aa8a202bded2affffbf55b56b92b565aadc4aa76fb6ddfffbfb56adb6683b3aaaab1445beeb75efbf6f6ad6da920db6aa8ab50b55bdaafdeffb555b5652b6552a2b5215afd6fddf7ddeab6abb44a5554b0dd416f56f6bfbefff5aadd6bad42d54b521755ba9ddfebef555555b46a652ab15447bed56bff7f7beaadb6dbf34a92adb10eddd596afeddfad6adb6aad468aa9621ffedaeb7fff7dd5ad55b1d94fa2b2a035fb693d5f6bd75556dadca515aaa9545bbfad4a9ffeefd56b6f6af447ea5550aeff52ad4ffb55555ab5bf56a96aaaa57bfdede52fdfaaab5556d5f575d5555557ffb6ba9efab55aaddb6eba7aea95295adaed5d4ffe97aad6adb7d96faa656a57fff766afeb6d6b6ad6daed3daa9192b9555b5adfffb7aaad7ab77eb7d52d4a56ffe9f54eed7dd556abdbabff5452295b56beb6afffaeb5b5ad6dfedbd549554abbd54baffdb7d6aadb5b5755eb4524abaa2b6eadffdd756eb5b7fdb5555292aadaeadb5fef77dbb5dedd575555124a556b55768affbdeed5536dfeaaaa8929abbead92adbeef7bf6bdb756a95524a5accd5554abfbfbaf7b56dafb6a524a95d75354925defb6fbdaeb6faea954255279daaa4c877fffdeff5ab5fbaaaa29295eaad5b25dfeedf7db6ddead524aa54557ef6d2a9fefff5dfdb6b5d5a9525554ad77b2d54f7fd5ff6f55db5aa548954937ddd5525bfbfff7fdeeb7eaa9552524ad6eeea94fff6f7ddfb5dd2d54a255b557b75ba52f7ffbfff6d6abd48a910b57aadbecb5bdeddfdedfb5f6556aaaaad5556d56dc5ffff77bfedb2daa9544576bd6d5557667fffdfffbaddb2a552aaaaeab7aa6d542ddb7ff6fd6b6a95542adfebdadb5491bfffffbff7b6fa52ab4b757aa56ad245577dbefbd95ba9552555dfacabb6aaa85df7fbef76d6da94aaa56aeb54aa4a564bdffffffd6d7a55552abb6d5b552a82557feffdaeb7a92aa4ab6cb6d554d15449ffbffffbad7ea4929576256aeb2a5255feffbfed76d7555555abdd2bb55129536ffffb76ab7a292aaabcabb6baea444bfffefffaddafc495554b7d56edba9555bffbffeb6bd12a556afb555b6ad6a852fbdfff75b6ada11555adaaabb76ea14bff7fedd6adaada4aaadaad4ad5ba9555dfffffaadad5552ab56ab6aafaa6d552fdff7fb6af2d5a954ab54955557aaa4af7f7fed5b5b5ad2aaaad556a574aaa553fdff75adaaab495555555452aae55a8bffddf56aead56492aaaa932ad75aa4aadb7ff6b7252a91295555589555554555fbafd55592a4488492deaaaaaaaa64ab68add5ad54d2a2324aa555d555552524eb57aab5aa54114956a9aa5556a195552aaab6d694a94a2c95556ab55a9464aaaa55555aa55200925555b55554429</textarea><br>
		<br>
	</div> -->

	<div id="log-box" style="position: absolute; right: 10px; top: 0px; width: 500px;height:50%;">
		日志：
		<br>
		<div id="log" style="width: 100%; height: 100%; overflow: scroll; border: solid 1px; padding: 20px;">

		</div>
	</div>
</body>

</html>